Skip to content

pesto: target address mapping and netavark integration#1004

Open
Honny1 wants to merge 3 commits into
podman-container-tools:mainfrom
Honny1:passta-and-pessto
Open

pesto: target address mapping and netavark integration#1004
Honny1 wants to merge 3 commits into
podman-container-tools:mainfrom
Honny1:passta-and-pessto

Conversation

@Honny1

@Honny1 Honny1 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
  • Add target address mapping to pesto CLI args so pasta forwards traffic directly to container IPs, removing the need for netavark DNAT rules.
  • portMappingsToPestoArgs now generates -t hostIP/hostPort:containerIP/containerPort
  • Add IPv6 --map-guest-addr for forwarding
  • Call PestoAddPorts/PestoDeletePorts from netavark Setup/Teardown
  • Skip netavark DNAT rules when pesto mode is active

Fixes: podman-container-tools/podman#28769
Fixes: podman-container-tools/podman#28770
Fixes: podman-container-tools/podman#28771

@github-actions github-actions Bot added the common Related to "common" package label Jul 21, 2026
@Honny1
Honny1 force-pushed the passta-and-pessto branch 2 times, most recently from c17020c to 5b996c5 Compare July 21, 2026 13:31
@Honny1
Honny1 marked this pull request as ready for review July 21, 2026 13:52
@Honny1
Honny1 force-pushed the passta-and-pessto branch from 5b996c5 to 1b2918e Compare July 21, 2026 14:57
Comment thread common/libnetwork/netavark/run.go Outdated
Comment on lines +168 to +169
logrus.Errorf("pesto: reading addr state: %v", err)
return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it would be better if return an actual error in this function and then in the end just let the caller log it once, that way we can skip the pesto prefix from each log here and just have the prefix once on the caller side.

// firstIPsWithNetwork picks the first IPv4 and IPv6 from the network IPs
// map (sorted by network name), returning the IP and the owning network.
func firstIPsWithNetwork(netIPs map[string]pestoNetworkIPs) (ipv4, ipv4Net, ipv6, ipv6Net string) {
for _, name := range sortedNetworkNames(netIPs) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a network order more or less given via the input network that are sorted I think we should respect that order and not sort based on the names.

Comment on lines +15 to +19
// pestoAddrState tracks which container IPs are currently used for pesto
// port forwarding. Stored as a per-container JSON file in the rootless
// netns directory so connect/disconnect operations can detect whether
// ports are already published and remap when necessary.
type pestoAddrState struct {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so I am thinking if we can avoid yet another extra way to keep the state here via the josn file, the podman db already carries the network result as state so I feel like adding onto that seems better as we then only store it in one location.

Of course it means we then need to pass the state from the podman db back into the teardown/setup functions but I feel like this could be a bit cleaner?

cc @mheon

Comment thread common/libnetwork/netavark/run.go Outdated
Comment on lines +172 to +175
if state == nil {
// Migration fallback: no state file means this container was set up
// before state tracking existed. Best-effort delete using IPAM IPs.
ipv4, ipv6 := containerIPsFromNetOpts(opts)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will gain us anything? The port format has changed so pasta would not consider them to be the same I think?

I guess there is not much do be done, we marked it as experimental so I am fine telling users they need to stop all containers after the update and only then start the new one which should kill and restart pasta to clean out the stale ports. (OR well just let them reboot)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is leftover after my mistake. I will drop that.

Comment thread common/libnetwork/netavark/run.go Outdated
Comment on lines +304 to +308
// In pesto mode, pasta handles the full forwarding path including target
// address mapping, so netavark doesn't need DNAT rules at all. Clear port
// mappings from the payload to avoid unnecessary iptables/nftables rules.
if n.rootlessPortForwarder == config.RootlessPortForwarderPasta && n.networkRootless {
opts.PortMappings = nil

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not drop the ports here, a user could still use podman unshare --rootless-netns curl 127.0.0.1:80 for example and that shoudl work. With nftables we batch update all rules anyway so this will not gain us anything noticeably

Comment thread common/libnetwork/pasta/pasta_linux.go Outdated
strings.Contains(string(out), "unrecognized option '"+mapGuestAddrOpt) &&
len(mapGuestAddrIPs) == 1 && mapGuestAddrIPs[0] == mapGuestAddrIpv4 {
// we did add the default --map-guest-addr option, if users set something different we want
len(mapGuestAddrIPs) == 2 && mapGuestAddrIPs[0] == mapGuestAddrIpv4 && mapGuestAddrIPs[1] == mapGuestAddrIpv6 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think pasta has supported --map-guest-addr for long enough now that we can hard depend on it. I would first simplify this by removing the retry code path. And then we do not need to special case this new argument here

@Honny1
Honny1 force-pushed the passta-and-pessto branch 2 times, most recently from abc89b3 to 8aa86af Compare July 23, 2026 15:54
@Honny1
Honny1 requested a review from Luap99 July 23, 2026 16:02
Fixes: podman-container-tools/podman#28770

Signed-off-by: Jan Rodák <hony.com@seznam.cz>
@Honny1
Honny1 force-pushed the passta-and-pessto branch from 8aa86af to c7b86fe Compare July 23, 2026 19:04
@Honny1

Honny1 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

PTAL @Luap99

Comment on lines +135 to +137
// When pasta restarts all forwarding rules are lost.
// pestoSetup detects this via pesto --show and re-publishes
// on the next Setup call.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't seem to be true, I guess there is nothing to be done here. I would just drop this comment.


// config is the containers.conf config, needed for FindHelperBinary
// when invoking pesto for dynamic port forwarding.
config *config.Config

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this is the first round of review but so far I tried to keep the full config out of the network interface as we lookup the netavark path once at the beginning and then store it as string instead.

I guess the is not strict reason to not have it so I am fine to add this here but it would allow us some cleanup/removal of fields above in theory. (anyhow lets not make this PR more complicated I guess, we can do that as some cleanup in the future)

Comment thread common/libnetwork/netavark/run.go Outdated
Comment on lines +148 to +160
opts := options.NetworkOptions
if opts.NetworkStatus == nil {
opts.NetworkStatus = result
}
if len(opts.NetworkOrder) == 0 {
opts.NetworkOrder = make([]string, 0, len(opts.Networks))
for _, net := range opts.Networks {
opts.NetworkOrder = append(opts.NetworkOrder, net.Name)
}
}
if err := n.pestoSetup(opts); err != nil {
return nil, err
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is nothing here to prevent a double add?

$ bin/podman run --name c1 --network net1 -p 8080:8080 -d  quay.io/libpod/testimage:20241011 sleep 1000
447eea8c3b4e8bb9ac2a35d4f03f34502d59757e95eb3029db9384b5286f42bd
$ bin/podman network connect net2 c1
Error: pesto failed: exit status 1
output: Forwarding configuration conflict: TCP [0.0.0.0]:8080  =>  10.89.2.5:8080  versus TCP [0.0.0.0]:8080  =>  10.89.2.5:8080 
Unable to add rule TCP [0.0.0.0]:8080  =>  10.89.2.5:8080 

If the network status is not nil we should assume the setup already happened on a prior network and do nothing

Comment thread common/libnetwork/pasta/pasta_linux.go Outdated
Comment on lines +43 to +52
mapGuestAddrIpv6 = "fc00::1"

// guestAddrIpv6 is assigned to the guest interface so the IPv6 gateway is reachable.
guestAddrIpv6 = "fc00::2"

// gatewayIpv6 is the IPv6 default gateway for the guest. pasta uses this as the source
// address for inbound IPv6 forwarding. Must differ from mapGuestAddrIpv6 to avoid
// --map-guest-addr intercepting reply traffic.
// See: https://bugs.passt.top/show_bug.cgi?id=217
gatewayIpv6 = "fc00::3"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer consistency with v4 that
mapGuestAddrIpv6 = fc00::2
then
gatewayIpv6 = fc00::1 (using first ip as gateway feels more natural)
guestAddrIpv6 = fc00::3

Comment thread common/libnetwork/pasta/pasta_linux.go Outdated
Comment on lines +278 to +308
cmdArgs = append(cmdArgs, mapGuestAddrOpt, mapGuestAddrIpv4, mapGuestAddrOpt, mapGuestAddrIpv6)
mapGuestAddrIPs = append(mapGuestAddrIPs, mapGuestAddrIpv4, mapGuestAddrIpv6)
}

// Pass --address and --gateway with non-link-local IPv6 addresses so pasta
// uses a routable source for inbound IPv6 forwarding (instead of link-local
// which is not routable across bridges). --address is required so the kernel
// can add the route to the gateway.
// See: https://bugs.passt.top/show_bug.cgi?id=217
hasIPv6Gateway := false
for _, ip := range gatewayIPs {
if strings.Contains(ip, ":") {
hasIPv6Gateway = true
break
}
}
if !hasIPv6Gateway {
hasIPv6Addr := false
for _, ip := range addressIPs {
if strings.Contains(ip, ":") {
hasIPv6Addr = true
break
}
}
if !hasIPv6Addr {
cmdArgs = append(cmdArgs, addressOpt, guestAddrIpv6)
}
cmdArgs = append(cmdArgs, gatewayOpt, gatewayIpv6)
}

cmdArgs = append(cmdArgs, "--netns", opts.Netns)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would try messing with ipv6 to much on the normal code path (the main --network pasta) hard coding the address there should not be needed

IMO the better thing isd move the extra options into the rootless netns setupPasta, i.e. where add the pid arg.
I think we should be fine hard coding the three options there. Though we would need to hard the v4 mapGuestAddrIpv4 there then as well.

Comment thread common/libnetwork/types/network.go Outdated
Comment on lines +321 to +326
NetworkStatus map[string]StatusBlock `json:"network_status,omitempty"`
// NetworkOrder lists network names in the order they were connected
// to the container (earliest first). Used by pesto to pick which
// container IP to forward traffic to: the first-connected network wins.
// Populated by the caller alongside NetworkStatus.
NetworkOrder []string `json:"network_order,omitempty"`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the struct as is is send to netavark via json, via do however do not need or want to send this.

My suggestion would be add json:"-" to both and add a comment that this is not part fo the netavark jsone format.

@Honny1
Honny1 force-pushed the passta-and-pessto branch from c7b86fe to e65c0d0 Compare July 24, 2026 15:11
@Honny1
Honny1 force-pushed the passta-and-pessto branch from e65c0d0 to 18c09ba Compare July 24, 2026 15:38

@Luap99 Luap99 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

MapGuestAddrIpv4 = mapGuestAddrIpv4
MapGuestAddrIpv6 = mapGuestAddrIpv6
GatewayIpv6 = gatewayIpv6
GuestAddrIpv6 = guestAddrIpv6

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose you might as well just export the variable without a second reassignment but that is just a minor style thing not worth blocking on.

@mheon

mheon commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

LGTM

@mheon
mheon enabled auto-merge July 24, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common Related to "common" package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IPv6 forwarding issues Destination address mapping in pesto Move pesto into libnetwork/netavark Setup()/Teardown()

3 participants